﻿using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
using System.Threading;//线程的命名空间

public class DataMgr : MonoBehaviour
{
    //存储接收到的数据
    public string r_message;

    //客户端Socket实例
    private Socket udpClient;

    //服务器终端 由IP+端口组成的结构
    private EndPoint serverPoint;

    //获取数据的线程
    private Thread getThread;

    //做一些数据的初始化,包括socket、本机Ip的获取,
    private void Start()
    {
        //创建Socket实例 指定Udp类型
        udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        //调用Dns.GetHostEntry的方法可以获得本机的IP
        //注意:客户端绑定的是本机IP,而服务端绑定的是公网IP
        //客户端的本地IP 会经过路由器 自动转化处理 所以客户端绑定的IP跟服务器实际打印出来的IP是不一样的.
        foreach (IPAddress _IPAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
        {
            if (_IPAddress.AddressFamily.ToString() == "InterNetwork")
            {
                //绑定客户端的IP和端口,如果不绑定,路由不会自动处理内网IP和外网IP的转换,使得只能发送数据,无法接收数据.
                udpClient.Bind(new IPEndPoint(_IPAddress, 7680));
            }
        }
        //这一个是服务器的IP和端口,通过IP找到远程主机,通过端口找到对应的服务端程序
        //serverPoint = new IPEndPoint(IPAddress.Parse("139.196.112.69"), 7679);
        serverPoint = new IPEndPoint(IPAddress.Parse("192.168.1.195"), 7679);
        //接收数据
        ReceiveData();
    }

    #region 发送消息
    public void Send(string str)
    {
        //将要发送的数据 转化为 byte[] 类型的数据
        byte[] data = Encoding.UTF8.GetBytes(str);
        //调用Socket实例的SendTo方法进行发送.
        udpClient.SendTo(data, serverPoint);//数据,发送的ip和端口号
    }
    #endregion

    #region 接收消息
    public void ReceiveData()
    {
        //开启一个线程接收数据 GetText在我们开启的线程里执行
        getThread = new Thread(GetText);
        //启动线程 其实就是调用GetText
        getThread.Start();

    }

    private void GetText()
    {
        //如果接收数据的线程不等于空 我们就循环的接收消息.
        while (getThread != null)
        {
            //any表示不需要指定,0也是表示不需要指定.等待接收数据后赋值. 发送数据过来的终端 Any表示任意IP的意思,0表示任意端口的意思
            //PS:如果在移动端,这个任意IP,是不能这样设置的,一定要明确的指定接收的IP
            EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
            //缓存数据的
            byte[] data = new byte[1024];
            //ref表示可以对后面的变量进行修改 length是返回数据的长度
            //这个方法会把数据的来源(ip和端口号)放到第二个数据上.
            int length = udpClient.ReceiveFrom(data, ref remoteEndPoint);
            //将接收的数据转化为string类型
            r_message = Encoding.UTF8.GetString(data, 0, length);
            if (r_message != null)
            {
                //如果接收到的数据不等于空,就打印到控制台
                Debug.Log("从ip:" + (remoteEndPoint as IPEndPoint).Address.ToString() + ":" +
                (remoteEndPoint as IPEndPoint).Port + ";收到了数据:" + r_message);
            }
        }
    }
    #endregion

    //这个方法,是指正常退出的时候才会调用,如果
    private void OnApplicationQuit()
    {
        //如果接收数据的线程没有终止,就终止线程
        if (getThread != null)
        {
            getThread.Abort();//如果不终止,程序有可能就卡住,失去响应
        }

        //如果Socket实例未关闭,就将它关闭
        if (udpClient != null)
        {
            udpClient.Close();
        }
    }
}
